#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "pila.h"

#define TAMANIO 10

/**********************************************************
*                         Prototipos
***********************************************************/

void ** redimensionar(pila_t *, size_t );
bool agrandar(pila_t *);
bool achicar(pila_t *);

/**********************************************************
*                       Implementacion
***********************************************************/

struct _pila {
    void** datos;
    size_t tamanio;
    size_t cantidad;
};

// Crea una pila.
// Post: devuelve una nueva pila vacía.
pila_t* pila_crear()
{
    pila_t* pila = malloc(sizeof(pila_t));
    if(!pila)
        return NULL;

    pila->datos = malloc(TAMANIO * sizeof(void *));

    if(!pila->datos)
	{
	    free(pila);
	    return NULL;
	}

	pila->tamanio = TAMANIO;

	pila->cantidad = 0;

	return pila;
}

// Destruye la pila.
// Pre: la pila fue creada.
// Post: se eliminaron todos los elementos de la pila.
void pila_destruir(pila_t *pila)
{
    free(pila->datos);
    free(pila);
}

// Devuelve verdadero o falso, según si la pila tiene o no elementos apilados.
// Pre: la pila fue creada.
bool pila_esta_vacia(const pila_t *pila)
{
    return (pila->cantidad == 0);
}

// Agrega un nuevo elemento a la pila. Devuelve falso en caso de error.
// Pre: la pila fue creada.
// Post: se agregó un nuevo elemento a la pila, valor es el nuevo tope.
bool pila_apilar(pila_t * pila, void* valor)
{
    if(pila->cantidad < pila->tamanio)
    {
        pila->datos[pila->cantidad] = valor;
        pila->cantidad++;
        return true;
    }

    // Agrandar
    if(!agrandar(pila))
    {
        //ERROR
        return false;
    }

    pila->datos[pila->cantidad] = valor;
    pila->cantidad++;
    return true;
}

// Aumentar en potencias de 2
bool agrandar(pila_t * pila)
{
    size_t nuevo_tamanio = pila->tamanio * 2;

    void ** p = redimensionar(pila,nuevo_tamanio);

    if(!p)
        return false;

    pila->datos = p;
    pila->tamanio = nuevo_tamanio;
    return true;
}

// Se divide por 2
bool achicar(pila_t * pila)
{
    size_t nuevo_tamanio = (pila->tamanio) / 2;

    void ** p = redimensionar(pila, nuevo_tamanio);

    if(!p)
        return false;

    pila->datos = p;
    pila->tamanio = nuevo_tamanio;
    return true;
}

void ** redimensionar(pila_t * pila, size_t nuevo_tamanio)
{
    void ** p = (void **) realloc(pila->datos,nuevo_tamanio * sizeof(void*));

    if(!p)
        return NULL;

    return p;
}

// Saca el elemento tope de la pila. Si la pila tiene elementos, se quita el
// tope de la pila, y se devuelve ese valor. Si la pila está vacía, devuelve
// NULL.
// Pre: la pila fue creada.
// Post: si la pila no estaba vacía, se devuelve el valor del tope anterior
// y la pila contiene un elemento menos.
void* pila_desapilar(pila_t *pila)
{
    if(pila->cantidad <= 0)
        return NULL;

    void * p = pila->datos[(pila->cantidad - 1)];
    pila->cantidad--;

    // Evaluo achicar al 25% del uso
    if((pila->cantidad > 0) && (pila->tamanio > 8))
    {
        int x = pila->tamanio / pila->cantidad;
        if(x == 4)
        {
            if(!achicar(pila))
            {
                printf("\n*** Error al achicar en tamanio = %d",pila->tamanio);
            }
        }
    }

    return p;
}

// Obtiene el valor del tope de la pila. Si la pila tiene elementos,
// se devuelve el valor del tope. Si está vacía devuelve NULL.
// Pre: la pila fue creada.
// Post: se devolvió el valor del tope de la pila, cuando la pila no está
// vacía, NULL en caso contrario.
void* pila_ver_tope(const pila_t *pila)
{
    if(pila->cantidad <= 0)
        return NULL;

    return pila->datos[pila->cantidad - 1];
}
